ICTSC2018 本戦 問題解説: Next Generation Networks(Baby)

問題文

さて、突然ですが超有名大企業ICTSC Comに入社した新入社員のあなたのミッションは、SRv6を使ってHostAからHostBへの通信の際にIDS(Snort)をサービスチェインして通るようにするということを任されました。
しかし現在はSRv6でHostAからHostBへ通信はIDS Nodeを通らずに通信をしてしまっています。
これをIDS Nodeを通ってからHostBに通るようしてほしい。

HostA: 10.0.0.1/24から HostB: 10.0.1.1/24にPingが飛ばせています。
この状態ではIDS Nodeを通らずHostAからHostBへSRv6を使ってルーティングされています。

IDS Nodeではnetwork namespaceで切り分けられたLinuxとSnortが内部に存在します。残念ながらSnortはディフォルトではSRに対応できていないため注意してください。またSnort自身の設定は変えなくても問題ないように設定されています。
そしてちゃんとパケットが流れればSnortのコンソールを確認するとアラートが流れることが確かめることができます。

以下にトポロジーと認証情報を添付するので確認してみてください。

情報

全てのホストは以下情報で入れます
ユーザー: admin
パスワード: hello_5g

以下のIPアドレスはManagementのための接続アドレスです。

Host A

IPアドレス: 192.168.12.6

Router 1

IPアドレス: 192.168.12.1

Router 2

IPアドレス: 192.168.12.2

Router 3

IPアドレス: 192.168.12.3

Router 4

IPアドレス: 192.168.12.4

IDS

IPアドレス: 192.168.12.5

Host B

IPアドレス: 192.168.12.7

構成

問題スタート

HostAからHostBにPingが飛ばせる。この状態ではAからBへSRv6を使ってルーティングされてる

問題ゴール

HostAからIDSを通ってHostBにPingが飛ばせる様になる。
IDSではICMPを認識して検出ができる.

注意事項

  • 必ずSRv6の機能をつかうようにしてください。
  • トラブルはSRだけとは限りません。あらゆる事がありえます。
  • SnortはIDSが立ち上がっている中でnetwork namespace(veth)で区切られている。以下の方法で簡単に確認ができます。

下には便利なチートシートです。

# srextを使うときはこれを実行してからではないと動きません(公式ドキュメントから抜粋)
sudo depmod -a
sudo modprobe srext

# vethにpacketが通ってきているか見たいとき
ip netns exec Snort tcpdump -i veth0-Snort

# Snort側のnamespaceに移る
ip netns exec Snort bash

# Snortでのアラートが見たいとき(exec Snort bashをした後に使える)
Snort -c /etc/Snort/Snort.conf -A console

トラブルの概要

SFCをしたいができていない。

解説

この問題は、2つのIPv4対応ホスト(hostA and hostB)がSegment routing over IPv6 dataplane(SRv6)を介して通信できるようにして、サービスチェインを実装する問題でした。

トポロジーの細かい要素としては以下のようになります。

  • Router
    • R1,R3,R4: SRv6をサポートするルーター
    • R2: v6だけサポートするルーター
  • IDS: namespaceで区切られたSnortが動作しているサーバ

実際にパケットの動きを見ていきましょう。

まず今回の基本設定の状態はAからBへのトラヒックはR1,R2,R3,R4を通じてBへ渡されます。
実際のフローを少し細かく文章化すると以下のようになります。

IPv4のペイロードがHostAからHostBへ送信される

IPv4のペイロードがHostAから送信されます。
ここではSegment routing header(以下SRH)は付与されず、宛先がHostBになっているIPv4パケットとして送信されます。

パケット構成は以下のようになります。

  • Ethernetヘッダ
  • IPv4
    • 送信元アドレス: HostA
    • 送信先アドレス: HostB

HostAから送信されたパケットがR1に到着

R1では以下の作業が行われます。

  • IPv6カプセル化をする
  • 2つのノードを対象にするヘッダであるSegment Routing Header(SRH)を挿入しカプセル化をする

SRHの中にはロケーター情報として、R3,R4を経由するという順序情報と、Segments Left(SL)というSRに対応したルーターを残りいくつ通るかの情報が記載されています。

この作業が終わった後のパケット構成は次のようになります。

  • Ethernetヘッダ
  • IPv6ヘッダ
    • 送信元アドレス: R1::1
    • 宛先アドレス: R3::bb
  • SRH
    • ロケーター情報: R4::bb, R3::bb
    • Segments Left(SL): 1
  • IPv4
    • 送信元アドレス: HostA
    • 送信先アドレス: HostB

上記情報から、IPv6ヘッダの指し示す宛先がR3であるため、R1のルーティングテーブルを見てパケットはR2へと転送されます。

R1から転送されたパケットがR2に到着

R2ではSRv6に対応するための設定は投入していません。
しかし、IPv6の経路情報はR2にインストールされているため、IPv6ルーティングによりR3に転送することができます。

R2から転送されたパケットがR3に到着

R3ではSRv6に対応するための設定が投入されているため、R3::bbに対応する操作を実行し、次の処理を行えるようにするためにEnd動作をします。
End動作により、IPv6ヘッダの値とSRHのSegments leftの値が書き換わります。

  • Ethernetヘッダ
  • IPv6ヘッダ
    • 送信元アドレス: R1::1
    • 宛先アドレス: R4::bb
  • SRH
    • ロケーター情報: R4::bb, R3::bb
    • Segments Left(SL): 0

この情報から、次はR4へとパケットを転送します。

R3から転送されたパケットがR4に到着

R4がSRv6網の終端となります。
この先のネットワークはIPv4ネットワークで、SRv6とIPv6のカプセル化を解除する必要があるため、End.DX4動作をしてカプセル化の解除を行います。

その際、具体的には外部のIPv6ヘッターを除去し、R3でSRv6の設定(function)により決められたIPv4 Next hopへとパケットを転送します。

  • Ethernetヘッダ
  • IPv4
    • 送信元アドレス: HostA
    • 送信先アドレス: HostB

上記の手順により、HostBにPing Requestを送信することができました!

次はPing Replyパケットが送信されるまでのフローを見ていきましょう。

問題出題状態では、R3を通らずに R4 -> R3 -> R1 のようにパケットが転送されるようになっています。

IPv4のペイロードがHostBからHostAへ送信される

IPv4のペイロードがHostBから送信されます。

  • Ethernetヘッダ
  • IPv4
    • 送信元アドレス: HostB
    • 送信先アドレス: HostA

HostBから送信されたパケットがR4に到着

R4では以下の作業が行われます。

  • IPv6カプセル化をする
  • 2つのノードを対象にするヘッダであるSegment Routing Header(SRH)を挿入しカプセル化をする
  • その後のパケットは以下のようになります

    • Ethernetヘッダ
    • IPv6ヘッダ
      • 送信元アドレス: R4::dd
      • 宛先アドレス: R1::1
    • SRH
      • ロケーター情報: R1::1
      • Segments Left(SL): 0
    • IPv4
      • 送信元アドレス: HostB
      • 送信先アドレス: HostA

    上記情報から、IPv6ヘッダの指し示す宛先がR3であるため、R1のルーティングテーブルを見てパケットはR2へと転送されます。

    R4から転送されたパケットがR2に到着

    R2ではSRv6に対応するための設定は投入していません。
    しかし、IPv6の経路情報はR2にインストールされているため、IPv6ルーティングによりR1に転送することができます。

    R2から転送されたパケットがR1に到着

    R1がSRv6網の終端となります。
    この先のネットワークはIPv4ネットワークで、SRv6とIPv6のカプセル化を解除する必要があるため、End.DX4動作をしてカプセル化の解除を行います。

    その際、具体的には外部のIPv6ヘッターを除去し、R3でSRv6の設定(function)により決められたIPv4 Next hopへとパケットを転送します。

    • Ethernetヘッダ
    • IPv4
      • 送信元アドレス: HostB
      • 送信先アドレス: HostA

    ここまでが基本的な状態の設定として問題環境に設定していた状態でした。
    SRv6について理解するためのきっかけになったと思います。

    では実際の問題を解くフェーズに入っていきます。

    今回の問題で行う方策をまとめると、

    • Snortがあるホスト(IDS)を通ってパケットを動かす必要がある
      • しかしSnortはSRv6に対応していない

    ということになります。

    (ちなみに 普通Snortは別ホストじゃないんですか? という質問については、この問題で使用するVMのリソースがコンテストで使用している物理サーバーのうち一台のサーバーをおおむね専有するほど多く、メモリを節約するためにしょうがなく行った苦肉の策でした。
    ですが出題としては全く問題ないのでこのまま出題しました。)

    今回のトポロジーから、Snortがあるホストにパケットを転送させるにはR1で付与するSRHのロケーター情報にR5を追加することで、R5を経由することができるようになります。

    しかし、SnortはSRv6に対応していません。そのため、一度SRv6のカプセリングを外してIPv4のパケットだけに変換する必要があります。

    「SRv6はデータプレーンにロケーション情報等が付与されているだけなので一旦外す」という方法が考えれると思います。
    ちょっと意地悪に見えるかなぁと思い、出題的には # srextを使うときはこれを実行してからではないと動きません(公式ドキュメントから抜粋) と書いておいたコードがありました。
    このコードについてインターネットで調べると、例えば

    いhttps://github.com/netgroup/SRv6-net-prog

    というリンクが見つかり、以下のようなそれっぽいfunctionを見つけることができます。

    • End.AD4: Endpoint to IPv4 SR-unaware APP via dynamic proxy
    • End.EAD4: Extended End.AD4 behavior that allow SR-unaware VNFS to be the last SF in SFC

    このことから、bbの定義を

    srconf localsid add fc00:5::bb end.ad4 ip 192.168.1.2 veth0 veth1

    このようにすることで、無事Snortまで通信が転送されます。Snortから戻ってきたパケットは正常にR4に流れていきます。
    別解としてEnd.EAD4を利用する方法もありますが、こちらでも問題はありません

    具体的な挙動としては、veth0に対して流れてきたパケットのSRHを外して、 IPv4 Payload オンリーにして流してあげるだけです。veth1に戻ってきたら、先程外したSRHを再度付与します。
    これで無事通信をIDSを通じてhostBへ行くことができました!

    問題作成の狙いとお気持ち

    この問題は次世代ネットワークについての知識をつけてほしいということから作りました。そもそもほぼ全員がSRv6ってなんやねんというところからで辛いかなと思ってたんですが、まずは存在を知ってもらって、その上でおもしろいところや有用性を理解してもらえたら嬉しいなと思って作った問題でした。また、狙いとしてSRv6はほぼmininet以外で遊べる環境というものは公開されていないため直接OSに対して書き込むconfigとかがあまり見ることができないというのがあります。なので今回は公開をすることでトラコン参加者以外も幸せになるおもしろい問題になったんじゃないかと思います。

    ちなみにですが実は自動起動の設定にsystemctlを使用したため、問題参加者はそのへんをガサゴソするとconfigがでてきて参考になったかも知れません。
    なおホントはもう少し難しい問題にする予定だったんですが、、、これではあまりにも解けなそうではと言われて変更して、変更したやつも、ちょっとむずかしいのでは?と言われてSRのコマンドだけの修正で解ける問に変更されたので Baby とつけていました。ちょっとpwn系のCTF問題みたいなネームみたいですね。
    それでも難しいと言われたので誰も挑戦してくれないかなとヒヤヒヤしていましたが、いくつかのチームは挑戦してくれたのでホッとしました笑

    回答例

    R1のSegment ID table(SID, つまり通るべき経路の指定)をR3,R5,R4の順番でオーダーする
    ip route add 10.0.1.0/24 encap seg6 mode encap segs fc00:3::bb,fc00:5::bb,fc00:4::bb dev eth1
    IDS NodeのSnortはSRHを処理することができません。よってIPv6 headerを取り外してあげる必要があります。よってサービスチェインのダイナミックプロキシを行う必要があります
    srconf localsid add fc00:5::bb end.ad4 ip 192.168.1.2 veth0 veth1
    ちなみにR1でR3,R4で指定してR3でR5に行くようにoverSRv6を更にしたらいいのではと思うかもしれないですが、拡張カーネルが必要で今回は対応していないため困難です

    これは今回出題したSRv6についての問題を体験できるVagrantFileです。もしよかったらぜひ遊んでみてください!!
    https://github.com/takehaya/SRv6_SFC_Example

    採点基準

    点数となるのはIDSNodeまでの中継するべきノードをSRv6でオーダーされていて50%+その後にダイナミックプロキシされるで満点を与える方針
    そもそもオーダーがない場合などは部分点をつけることはしません